/****************************************************************************
 * Copyright (C) 2012
 * by dj_skual
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you
 * must not claim that you wrote the original software. If you use
 * this software in a product, an acknowledgment in the product
 * documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and
 * must not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 * distribution.
 *
 * DevicesSwap.cpp
 *
 * for SaveGame Manager GX 2012
 ***************************************************************************/
#include <sdcard/wiisd_io.h>

#include "usb2storage.h"
#include "DevicesSwap.h"
#include "DeviceHandler.hpp"
#include "../Controls/Application.h"
#include "../Menu/Explorer.h"
#include "../Menu/TaskBar.hpp"
#include "../Prompts/SearchWindow.h"

DevicesSwap * DevicesSwap::instance = NULL;

DevicesSwap::DevicesSwap()
	: CThread(20)
	, ExitRequested(false)
	, SwapPorts(false)
	, ExplorerReload(false)
	, WaitingWindowOn(false)
	, usedIOS(IOS_GetVersion())
{
	Prompt = NULL;
	
	state = NULL;
	newState = NULL;
	
	StateInit();
	
	timer = new Timer();
	
	startThread();
}

DevicesSwap::~DevicesSwap()
{
	ExitRequested = true;
	
	shutdownThread();
	
	delete state;
	delete newState;
	
	delete timer;
}

void DevicesSwap::StateInit()
{
	GetConfigDevice();
	
	state = new StateStruct;
	newState = new StateStruct;
	
	state->sdInserted = DeviceHandler::Instance()->SD_Inserted();
	state->usb0Inserted = DeviceHandler::Instance()->USB0_Inserted();
	state->usb1Inserted = DeviceHandler::Instance()->USB1_Inserted();
	state->gcaInserted = DeviceHandler::Instance()->GCA_Inserted();
	state->gcbInserted = DeviceHandler::Instance()->GCB_Inserted();
}

void DevicesSwap::GetConfigDevice()
{
	std::string config_path = Settings.ConfigPath;
	config_path.erase(config_path.find_first_of(":"));
	
	for(int i = 0; i <= USB9; i++)
	{
		if(config_path.compare(DeviceName[i]))
			continue;
		
		ConfigDevice = i;
		break;
	}
	
	if(ConfigDevice != SD)
		ConfigDevice = DeviceHandler::Instance()->PartToPort(ConfigDevice);
}

void DevicesSwap::CheckState_SD()
{
	newState->sdInserted = false;
	
	if(state->sdInserted)
	{
		newState->sdInserted = __io_wiisd.isInserted();
	}
	else
	{
		if(ConfigDevice != SD)
		{
			if(__io_wiisd.startup())
			{
				newState->sdInserted = __io_wiisd.isInserted();
				__io_wiisd.shutdown();
			}
		}
		else
			newState->sdInserted = __io_wiisd.isInserted();
	}
	
	if(state->sdInserted != newState->sdInserted)
	{
		state->sdInserted = newState->sdInserted;
		
		if(!state->sdInserted)
		{
			if(ConfigDevice == SD)
				ShowWaitInsert();
			else
				DeviceHandler::Instance()->UnMountSD();
		}
		else
		{
			if(ConfigDevice == SD)
			{
				__io_wiisd.shutdown();
				DeviceHandler::Instance()->UnMountSD();
				DeviceHandler::Instance()->MountSD();
				
				CloseWaitInsert();
			}
			else
				DeviceHandler::Instance()->MountSD();
		}
		
		if(ConfigDevice != SD)
			Swapped(SD, state->sdInserted);
	}
}

void DevicesSwap::CheckState_USB0()
{
	if(Settings.USBPort == 1)
		return;
	
	newState->usb0Inserted = false;
	
	if (state->usb0Inserted)
	{
		newState->usb0Inserted = DeviceHandler::Instance()->USB0_Inserted();
	}
	else
	{
		(usedIOS > 200) ? __io_usbstorage2_port0.startup() : __io_usbstorage.startup();
		newState->usb0Inserted = (usedIOS > 200) ? __io_usbstorage2_port0.isInserted() : __io_usbstorage.isInserted();
		(usedIOS > 200) ? __io_usbstorage2_port0.shutdown() : __io_usbstorage.shutdown();
	}
	
	if(state->usb0Inserted != newState->usb0Inserted)
	{
		state->usb0Inserted = newState->usb0Inserted;
		
		if(!state->usb0Inserted)
		{
			if(ConfigDevice == USB0)
				ShowWaitInsert();
		}
		else 
		{
			DeviceHandler::Instance()->MountUSBPort0();
			
			if(ConfigDevice == USB0)
				CloseWaitInsert();
		}
		
		if(ConfigDevice != USB0)
			Swapped(USB0, state->usb0Inserted);
	}
}

void DevicesSwap::CheckState_USB1()
{
	if(Settings.USBPort == 0 || usedIOS <= 200)
		return;
	
	newState->usb1Inserted = false;
	
	if (state->usb1Inserted)
	{
		newState->usb1Inserted = DeviceHandler::Instance()->USB1_Inserted();
	}
	else
	{
		__io_usbstorage2_port1.startup();
		newState->usb1Inserted = __io_usbstorage2_port1.isInserted();
		__io_usbstorage2_port1.shutdown();
	}
	
	if(state->usb1Inserted != newState->usb1Inserted)
	{
		state->usb1Inserted = newState->usb1Inserted;
		
		if(!state->usb1Inserted)
		{
			if(ConfigDevice == USB1)
				ShowWaitInsert();
		}
		else 
		{
			DeviceHandler::Instance()->MountUSBPort1();
			
			if(ConfigDevice == USB1)
				CloseWaitInsert();
		}
		
		if(ConfigDevice != USB1)
			Swapped(USB1, state->usb1Inserted);
	}
}

void DevicesSwap::CheckState_GCA()
{
	newState->gcaInserted = false;
	
	if(state->gcaInserted)
	{
		newState->gcaInserted = DeviceHandler::Instance()->GCA_Inserted();
	}
	else
	{
		int ret = CARD_ProbeEx(CARD_SLOTA, NULL, NULL);
		if(ret != CARD_ERROR_WRONGDEVICE && ret != CARD_ERROR_NOCARD)
			newState->gcaInserted = true;
	}
	
	if(state->gcaInserted != newState->gcaInserted)
	{
		state->gcaInserted = newState->gcaInserted;
		
		if(!state->gcaInserted)
			DeviceHandler::Instance()->UnMountGCA();
		else
			DeviceHandler::Instance()->MountGCA();
		
		Swapped(GCA, state->gcaInserted);
	}
}

void DevicesSwap::CheckState_GCB()
{
	newState->gcbInserted = false;
	
	if(state->gcbInserted)
	{
		newState->gcbInserted = DeviceHandler::Instance()->GCB_Inserted();
	}
	else
	{
		int ret = CARD_ProbeEx(CARD_SLOTB, NULL, NULL);
		if(ret != CARD_ERROR_WRONGDEVICE && ret != CARD_ERROR_NOCARD)
			newState->gcbInserted = true;
	}
	
	if(state->gcbInserted != newState->gcbInserted)
	{
		state->gcbInserted = newState->gcbInserted;
		
		if(!state->gcbInserted)
			DeviceHandler::Instance()->UnMountGCB();
		else
			DeviceHandler::Instance()->MountGCB();
		
		Swapped(GCB, state->gcbInserted);
	}
}

void DevicesSwap::ShowWaitInsert()
{
	WaitingWindowOn = true;
	
	Prompt = new ThrobberWindow((ConfigDevice == SD) ? tr("SD Card removed.") : tr("USB Device removed."));
	Prompt->DimBackground(true);
	Application::Instance()->Append(Prompt);
	Application::Instance()->SetUpdateOnly(Prompt);
}

void DevicesSwap::CloseWaitInsert()
{
	if(Prompt)
	{
		Prompt->SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50);
		Prompt->Close();
	}
	
	WaitingWindowOn = false;
	
	if(ExplorerReload)
	{
		ExplorerReload = false;
		
		for(int dev = SD; dev <= USB9; ++dev)
		{
			if(!DeviceHandler::Instance()->IsInserted(dev))
				continue;
			
			Settings.LoadedBrowser = SDUSB_BROWSER;
			Settings.LastUsedPath = DeviceName[dev];
			Settings.LastUsedPath += ":/";
			break;
		}
		
		Explorer::Instance()->Reload();
		TaskBar::Instance()->UpdateAllButtons();
		
		CurrentBrowser()->FilterList();
		if(SearchWindow::Instance()->IsRunning())
			SearchWindow::Instance()->Refresh();
	}
}

void DevicesSwap::SwapUSB()
{
	if(Settings.USBPort != 2 && ConfigDevice != SD)
	{
		bool error = false;
		if(Settings.USBPort == 0 && ConfigDevice != USB0)
		{
			Settings.USBPort = 1;
			error = true;
		}
		else if(Settings.USBPort == 1 && ConfigDevice != USB1)
		{
			Settings.USBPort = 0;
			error = true;
		}
		
		if(error)
			ShowError(tr("Unable to unmount the Settings Device"));
	}
	
	DeviceHandler::Instance()->UnMountAllUSB();
	DeviceHandler::Instance()->MountAllUSB();
	
	if(DeviceHandler::PathToDriveType(CurrentBrowser()->GetCurrentPath()) != SD)
	{	
		for(int dev = SD; dev <= USB9; ++dev)
		{
			if(!DeviceHandler::Instance()->IsInserted(dev))
				continue;
			
			Settings.LastUsedPath = DeviceName[dev];
			Settings.LastUsedPath += ":/";
		}
	}
	
	StateInit();
	
	SwapPorts = false;
}

void DevicesSwap::executeThread(void)
{
	while(!ExitRequested)
	{
		if(SwapPorts)
			SwapUSB();
		
		if(timer->elapsed() < 1)
			continue;
		
		CheckState_SD();
		CheckState_USB0();
		CheckState_USB1();
		CheckState_GCA();
		CheckState_GCB();
		
		timer->reset();
	}
}
